home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_300 / 330_03 / tskttsk.c < prev    next >
C/C++ Source or Header  |  1990-10-10  |  9KB  |  297 lines

  1. /*
  2.    --- Version 2.2 90-10-12 10:34 ---
  3.  
  4.    TSKTTSK.C - CTask - Timer task.
  5.  
  6.    CTask - a Multitasking Kernel for C
  7.  
  8.    Public Domain Software written by
  9.       Thomas Wagner
  10.       Ferrari electronic Gmbh
  11.       Beusselstrasse 27
  12.       D-1000 Berlin 21
  13.       Germany
  14.  
  15.    No rights reserved.
  16.  
  17.    This file is new with 2.1. The timer task functions were moved
  18.    from tsktimer to this module.
  19.  
  20.    Timer logic has been significantly changed in version 2.0.
  21. */
  22.  
  23. #include "tsk.h"
  24. #include "tsklocal.h"
  25.  
  26. /*
  27.    tsk_timer_action 
  28.       performs the necessary action when a timeout occurred.
  29.       Starting with version 2.0, this part is not critical,
  30.       and may be preempted.
  31.  
  32.       Version 2.1 makes this routine global for use by the keyboard
  33.       hotkey handler.
  34. */
  35.  
  36. void Localfunc tsk_timer_action (tlinkptr elem)
  37. {
  38.    tcbptr task;
  39.    byte st;
  40.    CRITICAL;
  41.  
  42.    switch (elem->struckind)
  43.       {
  44.       case TKIND_WAKE:
  45.       case TKIND_TASK:  task = (tcbptr) elem->strucp;
  46.                         C_ENTER;
  47.                         st = task->state;
  48.  
  49.                         if (st == ST_WAITING || st == ST_DELAYED)
  50.                            {
  51.                            task->retptr = 
  52.                               ((elem->elkind & 0xf0) == TELEM_TIMER)
  53.                                  ? TTIMEOUT
  54.                                  : TWATCH;
  55.                            tsk_runable (task);
  56.                            }
  57.                         C_LEAVE;
  58.                         break;
  59.  
  60.       case TKIND_FLAG:  set_flag ((flagptr) elem->strucp);
  61.                         break;
  62.  
  63.       case TKIND_COUNTER: inc_counter ((counterptr) elem->strucp);
  64.                         break;
  65.  
  66.       case TKIND_COUNTDEC: dec_counter ((counterptr) elem->strucp);
  67.                         break;
  68.  
  69.       case TKIND_PROC:  tsk_callfunc (elem->strucp, elem);
  70.                         break;
  71.  
  72.       default:          break;
  73.       }
  74. }
  75.  
  76.  
  77. /*
  78.    tsk_exec_watch 
  79.       checks the watch condition, and returns 1 if the 
  80.       condition is met.
  81. */
  82.  
  83. local int Staticfunc tsk_exec_watch (tlinkptr curr)
  84. {
  85.    word val, cmp;
  86.    int elcmp;
  87.  
  88.    elcmp = curr->elkind & 0x0f;
  89.  
  90.    switch (curr->elkind & 0xf0)
  91.       {
  92.       case TELEM_MEM:   val = *(curr->elem.mem.address) & curr->elem.mem.mask;
  93.                         cmp = curr->elem.mem.compare;
  94.                         if (elcmp == TCMP_CHG)
  95.                            curr->elem.mem.compare = val;
  96.                         break;
  97.  
  98.       /* Microsoft C generates "Internal Compiler Error" on compiling
  99.          the following statement */
  100. #if (TSK_TURBO)
  101.       case TELEM_PORT:  val = (curr->elem.port.in_word)
  102.                               ? tsk_inpw (curr->elem.port.port)
  103.                               : (word)tsk_inp (curr->elem.port.port);
  104. #else
  105.       case TELEM_PORT:  if (curr->elem.port.in_word)
  106.                            val = tsk_inpw (curr->elem.port.port);
  107.                         else
  108.                            val = (word)tsk_inp (curr->elem.port.port);
  109. #endif
  110.                         val &= curr->elem.port.mask;
  111.                         cmp = curr->elem.port.compare;
  112.                         if (elcmp == TCMP_CHG)
  113.                            curr->elem.port.compare = val;
  114.                         break;
  115.  
  116.       default:          return 0;
  117.       }
  118.  
  119.    switch (elcmp)
  120.       {
  121.       case TCMP_EQ:  return val == cmp;
  122.       case TCMP_CHG:
  123.       case TCMP_NE:  return val != cmp;
  124.       case TCMP_LE:  return val <= cmp;
  125.       case TCMP_GE:  return val >= cmp;
  126.       case TCMP_LES: return (int)val <= (int)cmp;
  127.       case TCMP_GES: return (int)val >= (int)cmp;
  128.       default:       return 0;
  129.       }
  130. }
  131.  
  132.  
  133. /*
  134.    The timer task handles all timeouts.
  135.  
  136.    Starting with version 2.0, two queues are maintained, one for
  137.    the timeouts, and one for watch elements.
  138.  
  139.    The timeout queue now is ordered, with the tick count in the queue
  140.    element head holding the difference in ticks to the previous element.
  141.    Thus, only the first element of the timeout queue has to be counted
  142.    down, which will considerably speed up processing here if there are
  143.    multiple elements in the queue.
  144.  
  145.    The watch queue is unordered.
  146.  
  147.    The loop to check the queue elements is fully protected.
  148.    This allows other tasks access to the timer queue. The
  149.    concept used in pre-2.0 versions was pretty complicated to
  150.    handle, and still had to run with interupts disabled for
  151.    most parts.
  152.  
  153.    The new logic unchains expired timeout elements immediately, within
  154.    the protected loop, but delays processing them until after the loop 
  155.    is finished. Modification of those elements should not normally occur,
  156.    and will be rejected. The processing of the timeout/watch action can 
  157.    thus be handled with interrupts enabled.
  158.  
  159.    Version 2.1 corrects a bug that prevented wakeup elements from being
  160.    correctly processed if they had the repeat attribute. It introduces
  161.    a single link pointer in the timer control block for chaining the
  162.    elements to be acted upon, which is used exclusively by the timer
  163.    task. The 2.0 algorithm used the normal timer queue for chaining,
  164.    which convoluted the code, and, in the end, led to the mentioned bug.
  165.  
  166.    Version 2.2 cleans up processing by eliminating the "state" field,
  167.    all necessary info is now contained in the flags. This better supports
  168.    enabling and disabling elements while the timer task is processing them.
  169. */
  170.  
  171. void Taskfunc tsk_timer (void)
  172. {
  173.    queptr curr;
  174.    queptr help;
  175.    tlinkptr tlast;
  176.    tlinkptr tcurr;
  177.    byte flg;
  178.    CRITICAL;
  179.  
  180.    while (1)
  181.       {
  182.       wait_counter_set (&tsk_timer_counter, 0L);
  183.  
  184.       tlast = LNULL;
  185.  
  186.       C_ENTER;
  187.  
  188.       /* First, check the timeout queue. Since version 2.0, only
  189.          the first element has to be counted down.
  190.       */
  191.  
  192.       if (!((curr = GLOBDATA timer_queue.first)->kind & Q_HEAD))
  193.          if (!--curr->el.ticks)
  194.             {
  195.             /* Remove all counted-down elements from the timer queue,
  196.                marking them as not in queue and busy, and chaining them
  197.                through the special "next" pointer.
  198.                We can then process the elements outside the critical 
  199.                section.
  200.             */
  201.             do
  202.                {
  203.                help = curr;
  204.                curr = curr->next;
  205.                tsk_dequeue (help);
  206.                ((tlinkptr)help)->flags |= TFLAG_BUSY;
  207.                ((tlinkptr)help)->next = tlast;
  208.                tlast = (tlinkptr)help;
  209.                }
  210.             while (!(curr->kind & Q_HEAD) && !curr->el.ticks);
  211.             }
  212.  
  213.       C_LEAVE;
  214.       /* shortly allow interrupts */
  215.       C_ENTER;
  216.  
  217.       /* Now, check the watch queue. */
  218.  
  219.       for (curr = GLOBDATA watch_queue.first; !(curr->kind & Q_HEAD); )
  220.          {
  221.          help = curr;
  222.          curr = curr->next;
  223.  
  224.          if (tsk_exec_watch ((tlinkptr)help))
  225.             {
  226.             tsk_dequeue (help);
  227.             ((tlinkptr)help)->flags |= TFLAG_BUSY;
  228.             ((tlinkptr)help)->next = tlast;
  229.             tlast = (tlinkptr)help;
  230.             }
  231.          }
  232.  
  233.       /* Ready checking the queues, we can now re-enable interrupts
  234.          for execution of the timeout/watch action. Interrupts are
  235.          disabled only for a short period to check the state and
  236.          re-enqueue repeat elements.  */
  237.  
  238.       C_LEAVE;
  239.  
  240.       while (tlast != LNULL)
  241.          {
  242.          tcurr = tlast;
  243.          tlast = tlast->next;
  244.          tsk_timer_action (tcurr);
  245.  
  246.          C_ENTER;
  247.          flg = tcurr->flags;
  248.          if (flg & TFLAG_REMOVE)
  249.             {
  250.             if (!(flg & TFLAG_TEMP))
  251.                flg &= ~TFLAG_REMOVE;
  252.             }
  253.          else if (!(flg & TFLAG_UNQUEUE))
  254.             {
  255.             if (flg & (TFLAG_REPEAT | TFLAG_ENQUEUE))
  256.                {
  257.                if (tcurr->link.kind == TYP_TIMER)
  258.                   tsk_enqtimer ((queptr)tcurr, tcurr->elem.time.reload);
  259.                else
  260.                   tsk_putqueue (&GLOBDATA watch_queue, (queptr)tcurr);
  261.                }